From: Colin Walters Date: Wed, 5 Aug 2009 15:27:06 +0000 (-0400) Subject: ShellAppSystem: Support loading a .desktop file directly X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~1013^2^2~11129 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=9bd22dc0;p=gnome-shell.git ShellAppSystem: Support loading a .desktop file directly Previously, ShellAppSystem only loaded (and cached) the set of .desktop files from applications.menu and settings.menu, using the gnome-menus library. The ShellAppInfo structure was a "hidden typedef" for GMenuTreeEntry. But we need to support loading an arbitrary .desktop file. Thus, refactor the ShellAppInfo into a real struct, with a refcount, and allow it to point to either a GMenuTreeEntry or a GKeyFile. Also, in the case where we fail to lookup an icon for an application, ensure we return a 0 opacity texture. --- diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 1558f71f7..5ec1a4b26 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -278,7 +278,7 @@ AppDisplay.prototype = { _getMostUsed: function() { let context = ""; return this._appMonitor.get_most_used_apps(context, 30).map(Lang.bind(this, function (id) { - return this._appSystem.lookup_app(id); + return this._appSystem.lookup_cached_app(id); })).filter(function (e) { return e != null }); }, @@ -774,7 +774,7 @@ AppWell.prototype = { let result = []; for (let i = 0; i < appIds.length; i++) { let id = appIds[i]; - let app = this._appSystem.lookup_app(id); + let app = this._appSystem.lookup_cached_app(id); if (!app) continue; result.push(app); diff --git a/js/ui/widget.js b/js/ui/widget.js index b3326576a..b33028a97 100644 --- a/js/ui/widget.js +++ b/js/ui/widget.js @@ -318,7 +318,7 @@ AppsWidget.prototype = { let appSystem = Shell.AppSystem.get_default(); let apps = appSystem.get_favorites(); for (let i = 0; i < apps.length; i++) { - let app = appSystem.lookup_app(apps[i]); + let app = appSystem.lookup_cached_app(apps[i]); if (!app) continue; this.addItem(new AppsWidgetInfo(app)); diff --git a/src/shell-app-system.c b/src/shell-app-system.c index cee5fc8fc..3ac35b3e8 100644 --- a/src/shell-app-system.c +++ b/src/shell-app-system.c @@ -54,16 +54,80 @@ static void reread_favorite_apps (ShellAppSystem *system); G_DEFINE_TYPE(ShellAppSystem, shell_app_system, G_TYPE_OBJECT); +typedef enum { + SHELL_APP_INFO_TYPE_ENTRY, + SHELL_APP_INFO_TYPE_DESKTOP_FILE +} ShellAppInfoType; + +struct _ShellAppInfo { + ShellAppInfoType type; + + /* We need this for two reasons. First, GKeyFile doesn't have a refcount. + * http://bugzilla.gnome.org/show_bug.cgi?id=590808 + * + * But more generally we'll always need it so we know when to free this + * structure (short of weak references on each item). + */ + guint refcount; + + GMenuTreeItem *entry; + + GKeyFile *keyfile; + char *keyfile_path; +}; + ShellAppInfo* shell_app_info_ref (ShellAppInfo *info) { - return gmenu_tree_item_ref ((GMenuTreeItem*)info); + info->refcount++; + return info; } void shell_app_info_unref (ShellAppInfo *info) { - gmenu_tree_item_unref ((GMenuTreeItem *)info); + if (--info->refcount > 0) + return; + switch (info->type) + { + case SHELL_APP_INFO_TYPE_ENTRY: + gmenu_tree_item_unref (info->entry); + break; + case SHELL_APP_INFO_TYPE_DESKTOP_FILE: + g_key_file_free (info->keyfile); + g_free (info->keyfile_path); + break; + } + g_slice_free (ShellAppInfo, info); +} + +static ShellAppInfo * +shell_app_info_new_from_tree_item (GMenuTreeItem *item) +{ + ShellAppInfo *info; + + if (!item) + return NULL; + + info = g_slice_alloc (sizeof (ShellAppInfo)); + info->type = SHELL_APP_INFO_TYPE_ENTRY; + info->refcount = 1; + info->entry = gmenu_tree_item_ref (item); + return info; +} + +static ShellAppInfo * +shell_app_info_new_from_keyfile_take_ownership (GKeyFile *keyfile, + const char *path) +{ + ShellAppInfo *info; + + info = g_slice_alloc (sizeof (ShellAppInfo)); + info->type = SHELL_APP_INFO_TYPE_DESKTOP_FILE; + info->refcount = 1; + info->keyfile = keyfile; + info->keyfile_path = g_strdup (path); + return info; } static gpointer @@ -237,7 +301,8 @@ gather_entries_recurse (ShellAppSystem *monitor, { case GMENU_TREE_ITEM_ENTRY: { - apps = g_slist_prepend (apps, item); + ShellAppInfo *app = shell_app_info_new_from_tree_item (item); + apps = g_slist_prepend (apps, app); } break; case GMENU_TREE_ITEM_DIRECTORY: @@ -245,12 +310,11 @@ gather_entries_recurse (ShellAppSystem *monitor, GMenuTreeDirectory *dir = (GMenuTreeDirectory*)item; apps = gather_entries_recurse (monitor, apps, dir); } - gmenu_tree_item_unref (item); break; default: - gmenu_tree_item_unref (item); break; } + gmenu_tree_item_unref (item); } g_slist_free (contents); @@ -285,7 +349,7 @@ cache_by_id (ShellAppSystem *self, GSList *apps, gboolean ref) { ShellAppInfo *info = iter->data; if (ref) - gmenu_tree_item_ref ((GMenuTreeItem*) info); + shell_app_info_ref (info); /* the name is owned by the info itself */ g_hash_table_insert (self->priv->app_id_to_app, (char*)shell_app_info_get_id (info), info); @@ -557,14 +621,52 @@ shell_app_system_remove_favorite (ShellAppSystem *system, const char *id) * Return value: (transfer full): The #ShellAppInfo for id, or %NULL if none */ ShellAppInfo * -shell_app_system_lookup_app (ShellAppSystem *self, const char *id) +shell_app_system_lookup_cached_app (ShellAppSystem *self, const char *id) +{ + ShellAppInfo *info; + + info = g_hash_table_lookup (self->priv->app_id_to_app, id); + if (info) + shell_app_info_ref (info); + return info; +} + +ShellAppInfo * +shell_app_system_load_from_desktop_file (ShellAppSystem *system, + const char *filename, + GError **error) { - GMenuTreeEntry *entry; + ShellAppInfo *appinfo; + GKeyFile *keyfile; + char *full_path = NULL; + gboolean success; + + keyfile = g_key_file_new (); + + if (strchr (filename, '/') != NULL) + { + success = g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, error); + full_path = g_strdup (filename); + } + else + { + char *app_path = g_build_filename ("applications", filename, NULL); + success = g_key_file_load_from_data_dirs (keyfile, app_path, &full_path, + G_KEY_FILE_NONE, error); + g_free (app_path); + } + + if (!success) + { + g_key_file_free (keyfile); + g_free (full_path); + return NULL; + } + + appinfo = shell_app_info_new_from_keyfile_take_ownership (keyfile, full_path); + g_free (full_path); - entry = g_hash_table_lookup (self->priv->app_id_to_app, id); - if (entry) - gmenu_tree_item_ref ((GMenuTreeItem*) entry); - return (ShellAppInfo*)entry; + return appinfo; } /** @@ -584,7 +686,7 @@ shell_app_system_lookup_heuristic_basename (ShellAppSystem *system, char *tmpid; ShellAppInfo *result; - result = shell_app_system_lookup_app (system, name); + result = shell_app_system_lookup_cached_app (system, name); if (result != NULL) return result; @@ -593,13 +695,13 @@ shell_app_system_lookup_heuristic_basename (ShellAppSystem *system, * prefix. So try stripping them. */ tmpid = g_strjoin ("", "gnome-", name, NULL); - result = shell_app_system_lookup_app (system, tmpid); + result = shell_app_system_lookup_cached_app (system, tmpid); g_free (tmpid); if (result != NULL) return result; tmpid = g_strjoin ("", "fedora-", name, NULL); - result = shell_app_system_lookup_app (system, tmpid); + result = shell_app_system_lookup_cached_app (system, tmpid); g_free (tmpid); if (result != NULL) return result; @@ -610,37 +712,79 @@ shell_app_system_lookup_heuristic_basename (ShellAppSystem *system, const char * shell_app_info_get_id (ShellAppInfo *info) { - return gmenu_tree_entry_get_desktop_file_id ((GMenuTreeEntry*)info); + switch (info->type) + { + case SHELL_APP_INFO_TYPE_ENTRY: + return gmenu_tree_entry_get_desktop_file_id ((GMenuTreeEntry*)info->entry); + case SHELL_APP_INFO_TYPE_DESKTOP_FILE: + return info->keyfile_path; + } + g_assert_not_reached (); + return NULL; } -const char * +#define DESKTOP_ENTRY_GROUP "Desktop Entry" + +char * shell_app_info_get_name (ShellAppInfo *info) { - return gmenu_tree_entry_get_name ((GMenuTreeEntry*)info); + switch (info->type) + { + case SHELL_APP_INFO_TYPE_ENTRY: + return g_strdup (gmenu_tree_entry_get_name ((GMenuTreeEntry*)info->entry)); + case SHELL_APP_INFO_TYPE_DESKTOP_FILE: + return g_key_file_get_locale_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Name", NULL, NULL); + } + g_assert_not_reached (); + return NULL; } -const char * +char * shell_app_info_get_description (ShellAppInfo *info) { - return gmenu_tree_entry_get_comment ((GMenuTreeEntry*)info); + switch (info->type) + { + case SHELL_APP_INFO_TYPE_ENTRY: + return g_strdup (gmenu_tree_entry_get_comment ((GMenuTreeEntry*)info->entry)); + case SHELL_APP_INFO_TYPE_DESKTOP_FILE: + return g_key_file_get_locale_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Comment", NULL, NULL); + } + g_assert_not_reached (); + return NULL; } -const char * +char * shell_app_info_get_executable (ShellAppInfo *info) { - return gmenu_tree_entry_get_exec ((GMenuTreeEntry*)info); + switch (info->type) + { + case SHELL_APP_INFO_TYPE_ENTRY: + return g_strdup (gmenu_tree_entry_get_exec ((GMenuTreeEntry*)info->entry)); + case SHELL_APP_INFO_TYPE_DESKTOP_FILE: + return g_key_file_get_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Exec", NULL); + } + g_assert_not_reached (); + return NULL; } -const char * +char * shell_app_info_get_desktop_file_path (ShellAppInfo *info) { - return gmenu_tree_entry_get_desktop_file_path ((GMenuTreeEntry*)info); + switch (info->type) + { + case SHELL_APP_INFO_TYPE_ENTRY: + return g_strdup (gmenu_tree_entry_get_desktop_file_path ((GMenuTreeEntry*)info->entry)); + case SHELL_APP_INFO_TYPE_DESKTOP_FILE: + return g_strdup (info->keyfile_path);; + } + g_assert_not_reached (); + return NULL; } GIcon * shell_app_info_get_icon (ShellAppInfo *info) { - const char *iconname; + char *iconname; GIcon *icon; /* This code adapted from gdesktopappinfo.c @@ -649,7 +793,16 @@ shell_app_info_get_icon (ShellAppInfo *info) * LGPL */ - iconname = gmenu_tree_entry_get_icon ((GMenuTreeEntry*)info); + switch (info->type) + { + case SHELL_APP_INFO_TYPE_ENTRY: + iconname = g_strdup (gmenu_tree_entry_get_icon ((GMenuTreeEntry*)info->entry)); + break; + case SHELL_APP_INFO_TYPE_DESKTOP_FILE: + iconname = g_key_file_get_locale_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Icon", NULL, NULL); + break; + } + if (!iconname) return NULL; @@ -677,6 +830,8 @@ shell_app_info_get_icon (ShellAppInfo *info) icon = g_themed_icon_new (tmp_name); g_free (tmp_name); } + g_free (iconname); + return icon; } @@ -689,7 +844,15 @@ shell_app_info_get_categories (ShellAppInfo *info) gboolean shell_app_info_get_is_nodisplay (ShellAppInfo *info) { - return gmenu_tree_entry_get_is_nodisplay ((GMenuTreeEntry*)info); + switch (info->type) + { + case SHELL_APP_INFO_TYPE_ENTRY: + return gmenu_tree_entry_get_is_nodisplay ((GMenuTreeEntry*)info); + case SHELL_APP_INFO_TYPE_DESKTOP_FILE: + return FALSE; + } + g_assert_not_reached (); + return TRUE; } /** @@ -710,7 +873,7 @@ shell_app_info_create_icon_texture (ShellAppInfo *info, float size) if (!icon) { ret = clutter_texture_new (); - g_object_set (ret, "width", size, "height", size, NULL); + g_object_set (ret, "opacity", 0, "width", size, "height", size, NULL); return ret; } @@ -734,7 +897,7 @@ shell_app_info_launch_full (ShellAppInfo *info, GError **error) { GDesktopAppInfo *gapp; - const char *filename; + char *filename; GdkAppLaunchContext *context; gboolean ret; ShellGlobal *global; @@ -744,9 +907,10 @@ shell_app_info_launch_full (ShellAppInfo *info, if (startup_id) *startup_id = NULL; - filename = gmenu_tree_entry_get_desktop_file_path ((GMenuTreeEntry*) info); - + filename = shell_app_info_get_desktop_file_path (info); gapp = g_desktop_app_info_new_from_filename (filename); + g_free (filename); + if (!gapp) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Not found"); diff --git a/src/shell-app-system.h b/src/shell-app-system.h index 0ec6861fe..7691eafaf 100644 --- a/src/shell-app-system.h +++ b/src/shell-app-system.h @@ -45,19 +45,19 @@ struct _ShellAppMenuEntry { GType shell_app_menu_entry_get_type (void); -/* Hidden typedef for a GMenuTreeEntry */ typedef struct _ShellAppInfo ShellAppInfo; +#define SHELL_TYPE_APP_INFO (shell_app_info_get_type ()) GType shell_app_info_get_type (void); ShellAppInfo* shell_app_info_ref (ShellAppInfo *info); void shell_app_info_unref (ShellAppInfo *info); const char *shell_app_info_get_id (ShellAppInfo *info); -const char *shell_app_info_get_name (ShellAppInfo *info); -const char *shell_app_info_get_description (ShellAppInfo *info); -const char *shell_app_info_get_executable (ShellAppInfo *info); -const char *shell_app_info_get_desktop_file_path (ShellAppInfo *info); +char *shell_app_info_get_name (ShellAppInfo *info); +char *shell_app_info_get_description (ShellAppInfo *info); +char *shell_app_info_get_executable (ShellAppInfo *info); +char *shell_app_info_get_desktop_file_path (ShellAppInfo *info); GIcon *shell_app_info_get_icon (ShellAppInfo *info); ClutterActor *shell_app_info_create_icon_texture (ShellAppInfo *info, float size); GSList *shell_app_info_get_categories (ShellAppInfo *info); @@ -71,7 +71,9 @@ gboolean shell_app_info_launch_full (ShellAppInfo *info, gboolean shell_app_info_launch (ShellAppInfo *info, GError **error); -ShellAppInfo *shell_app_system_lookup_app (ShellAppSystem *system, const char *id); +ShellAppInfo *shell_app_system_load_from_desktop_file (ShellAppSystem *system, const char *filename, GError **error); + +ShellAppInfo *shell_app_system_lookup_cached_app (ShellAppSystem *system, const char *id); ShellAppInfo *shell_app_system_lookup_heuristic_basename (ShellAppSystem *system, const char *id);